package com.zfzs.post.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.apache.commons.collections4.map.LinkedMap;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
* redis 工具类
* @author: ck
* @create: 2019/8/9/18:00
**/
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        String result = null;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            result = operations.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, String value) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, String value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            // 负数过期时间则永不过期
            if (expireTime != null && expireTime > 0L) {
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            }
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * hset
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public boolean hset(String key, String field, String value, long... expire) {
        boolean result = false;
        try {
            final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
            final byte[] rawField = redisTemplate.getStringSerializer().serialize(field);
            final byte[] rawValue = redisTemplate.getStringSerializer().serialize(value);
            result = redisTemplate.execute(new RedisCallback<Boolean>() {
                public Boolean doInRedis(RedisConnection connection) {
                    boolean ret = connection.hSet(rawKey, rawField, rawValue);
                    if (expire.length > 0 && expire[0] > 0) {
                        connection.expire(rawKey, expire[0]);
                    }
                    return ret;
                }
            }, true);
        } catch (Exception ex) {
        }
        return result;
    }

    /**
     * hget
     *
     * @param key
     * @param field
     * @return
     */
    public String hget(String key, String field) {
        final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
        final byte[] rawField = redisTemplate.getStringSerializer().serialize(field);
        final byte[] rawValue = redisTemplate.execute(new RedisCallback<byte[]>() {
            public byte[] doInRedis(RedisConnection connection) {
                return connection.hGet(rawKey, rawField);
            }
        }, true);
        return redisTemplate.getStringSerializer().deserialize(rawValue);
    }

    /**
     * hgetAll
     *
     * @param key
     * @return
     */
    public Map<String, String> hgetAll(String key) {
        final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
        final Map<byte[], byte[]> rawFieldValueMap = redisTemplate.execute(new RedisCallback<Map<byte[], byte[]>>() {
            public Map<byte[], byte[]> doInRedis(RedisConnection connection) {
                return connection.hGetAll(rawKey);
            }
        }, true);
        if (rawFieldValueMap == null) {
            return null;
        }
        Map<String, String> fieldValueMap = new LinkedMap<>();
        for (Entry<byte[], byte[]> entry : rawFieldValueMap.entrySet()) {
            fieldValueMap.put(redisTemplate.getStringSerializer().deserialize(entry.getKey()),
                    redisTemplate.getStringSerializer().deserialize(entry.getValue()));
        }
        return fieldValueMap;
    }

    /**
     * hIncr 原子自增
     *
     * @param key
     * @param field
     * @return
     */
    public Long hIncr(String key, String field, long... expire) {
        return hIncr(1L, key, field, expire);
    }

    /**
     * hIncr 原子自增
     *
     * @param key
     * @param field
     * @param delta 自增的量
     * @return
     */
    public Long hIncr(long delta, String key, String field, long... expire) {
        final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
        final byte[] rawField = redisTemplate.getStringSerializer().serialize(field);
        return redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection) {
                Long ret = connection.hIncrBy(rawKey, rawField, delta);
                if (expire.length > 0 && expire[0] > 0) {
                    connection.expire(rawKey, expire[0]);
                }
                return ret;
            }
        }, true);
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 将列表保存到缓存
     *
     * @param key    缓存key
     * @param expire 缓存时间
     * @param data   列表数据
     */
    public <T> void setList2Cache(String key, long expire, List<T> data) {
        this.set(key, JSON.toJSONString(data), expire);
    }

    /**
     * 从缓存中获取列表
     *
     * @param key   缓存key
     * @param clazz
     * @return
     */
    public <T> List<T> getListFromCache(String key, Class<T> clazz) {
        List<T> data = new ArrayList<T>();
        String result = this.get(key);
        if (StringUtils.isBlank(result)) {
            return data;
        }
        data = getListByJsonStr(result, clazz);
        return data;
    }

    /**
     * 从缓存中获取列表
     *
     * @param jsonStr
     * @param clazz
     * @return
     */
    public <T> List<T> getListByJsonStr(String jsonStr, Class<T> clazz) {
        List<T> data = new ArrayList<T>();
        if (StringUtils.isBlank(jsonStr)) {
            return data;
        }
        try {
            JSONArray jsonArray = JSONArray.parseArray(jsonStr);
            if (jsonArray != null && jsonArray.size() > 0) {
                for (int i = 0; i < jsonArray.size(); i++) {
                    T elem = JSON.parseObject(jsonArray.get(i).toString(), clazz);
                    data.add(elem);
                }
            }
        } catch (Exception ex) {
        }

        return data;
    }

    /**
     * 将对象保存到缓存
     *
     * @param key    缓存key
     * @param expire 缓存时间
     * @param object 对象
     */
    public <T> void setObject2Cache(String key, long expire, T object) {
        this.set(key, JSON.toJSONString(object), expire);
    }

    /**
     * 从缓存中获取对象
     *
     * @param key   缓存key
     * @param clazz
     * @return
     */
    public <T> T getObjectFromCache(String key, Class<T> clazz) {
        String result = get(key);
        if (StringUtils.isBlank(result)) {
            return null;
        }
        T elem = null;
        try {
            elem = JSON.parseObject(result, clazz);
        } catch (Exception ex) {
        }
        return elem;
    }

    public interface GetData<T> {
        T get();
    }

    public interface GetListData<T> {
        List<T> get();
    }

    /**
     * 把一个对象缓存到hash中
     *
     * @param key
     * @param obj
     * @param expire
     * @return
     */
    public Boolean hsetObj(String key, Object obj, long expire) {
        HashMap parseObject = JSON.parseObject(JSON.toJSONString(obj), HashMap.class);
        Map<byte[], byte[]> byteMap = new HashMap<byte[], byte[]>();
        Set<Entry> entrySet = parseObject.entrySet();
        for (Entry entry : entrySet) {
            byte[] keyBytes = redisTemplate.getStringSerializer().serialize(entry.getKey().toString());
            byte[] valueBytes = redisTemplate.getStringSerializer().serialize(entry.getValue().toString());
            byteMap.put(keyBytes, valueBytes);
        }
        final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) {
                connection.hMSet(rawKey, byteMap);
                if (expire > 0) {
                    connection.expire(rawKey, expire);
                }
                return true;
            }
        }, true);
    }

    /**
     * 在一个hash中获得一个对象
     *
     * @param key
     * @param clazzType
     * @return
     */
    public <T> T hgetObj(String key, Class<T> clazzType) {
        final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
        final Map<byte[], byte[]> byteMap = redisTemplate.execute(new RedisCallback<Map<byte[], byte[]>>() {
            public Map<byte[], byte[]> doInRedis(RedisConnection connection) {
                return connection.hGetAll(rawKey);
            }
        }, true);
        if (byteMap == null) {
            return null;
        }
        if (byteMap.isEmpty()) {
            return null;
        }
        Set<Entry<byte[], byte[]>> entrySet = byteMap.entrySet();
        Map<String, String> resMap = new HashMap<String, String>();
        for (Entry<byte[], byte[]> entry : entrySet) {
            String fKey = redisTemplate.getStringSerializer().deserialize(entry.getKey());
            String fValue = redisTemplate.getStringSerializer().deserialize(entry.getValue());
            resMap.put(fKey, fValue);
        }
        T t = null;
        try {
            t = JSON.parseObject(JSON.toJSONString(resMap), clazzType);
        } catch (Exception ex) {
        }
        return t;
    }

    /**
     * hash原子递减
     *
     * @param key
     * @param field
     * @param expire
     */
    public void hDec(String key, String field, long expire) {
        hIncr(-1L, key, field, expire);
    }

    public void expireKey(String key, long livetime) {
        redisTemplate.expire(key, livetime, TimeUnit.SECONDS.SECONDS);
    }

    public long incr(String key) {
        return incr(key, 1);
    }

    public long incr(String key, long delta) {
        Long increment = redisTemplate.opsForValue().increment(key, delta);
        return increment;
    }

    public long incr(String key, long delta, long liveTime) {
        long incr = incr(key, delta);
        if (incr == 1) {
            expireKey(key, liveTime);
        }
        return incr;
    }

    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    public void removeKes(Set<String> keys) {
        redisTemplate.delete(keys);
    }

}
